/*
 * $Id: BasicTaskPaneUI.java 4158 2012-02-03 18:29:40Z kschaefe $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.plaf.basic;

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicGraphicsUtils;

import org.jdesktop.swingx.JXCollapsiblePane;
import org.jdesktop.swingx.JXHyperlink;
import org.jdesktop.swingx.JXTaskPane;
import org.jdesktop.swingx.SwingXUtilities;
import org.jdesktop.swingx.icon.EmptyIcon;
import org.jdesktop.swingx.plaf.TaskPaneUI;

/**
 * Base implementation of the <code>JXTaskPane</code> UI.
 * 
 * @author <a href="mailto:fred@L2FProd.com">Frederic Lavigne</a>
 */
public class BasicTaskPaneUI extends TaskPaneUI {

	private static FocusListener focusListener = new RepaintOnFocus();

	public static ComponentUI createUI(JComponent c) {
		return new BasicTaskPaneUI();
	}

	protected int titleHeight = 25;
	protected int roundHeight = 5;

	protected JXTaskPane group;

	protected boolean mouseOver;
	protected MouseInputListener mouseListener;

	protected PropertyChangeListener propertyListener;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void installUI(JComponent c) {
		super.installUI(c);
		group = (JXTaskPane) c;

		installDefaults();
		installListeners();
		installKeyboardActions();
	}

	/**
	 * Installs default properties. Following properties are installed:
	 * <ul>
	 * <li>TaskPane.background</li>
	 * <li>TaskPane.foreground</li>
	 * <li>TaskPane.font</li>
	 * <li>TaskPane.borderColor</li>
	 * <li>TaskPane.titleForeground</li>
	 * <li>TaskPane.titleBackgroundGradientStart</li>
	 * <li>TaskPane.titleBackgroundGradientEnd</li>
	 * <li>TaskPane.titleOver</li>
	 * <li>TaskPane.specialTitleOver</li>
	 * <li>TaskPane.specialTitleForeground</li>
	 * <li>TaskPane.specialTitleBackground</li>
	 * </ul>
	 */
	protected void installDefaults() {
		LookAndFeel.installProperty(group, "opaque", true);
		group.setBorder(createPaneBorder());
		((JComponent) group.getContentPane()).setBorder(createContentPaneBorder());

		LookAndFeel.installColorsAndFont(group, "TaskPane.background", "TaskPane.foreground", "TaskPane.font");

		LookAndFeel.installColorsAndFont((JComponent) group.getContentPane(), "TaskPane.background", "TaskPane.foreground",
				"TaskPane.font");
	}

	/**
	 * Installs listeners for UI delegate.
	 */
	protected void installListeners() {
		mouseListener = createMouseInputListener();
		group.addMouseMotionListener(mouseListener);
		group.addMouseListener(mouseListener);

		group.addFocusListener(focusListener);
		propertyListener = createPropertyListener();
		group.addPropertyChangeListener(propertyListener);
	}

	/**
	 * Installs keyboard actions to allow task pane to react on hot keys.
	 */
	protected void installKeyboardActions() {
		InputMap inputMap = (InputMap) UIManager.get("TaskPane.focusInputMap");
		if (inputMap != null) {
			SwingUtilities.replaceUIInputMap(group, JComponent.WHEN_FOCUSED, inputMap);
		}

		ActionMap map = getActionMap();
		if (map != null) {
			SwingUtilities.replaceUIActionMap(group, map);
		}
	}

	ActionMap getActionMap() {
		ActionMap map = new ActionMapUIResource();
		map.put("toggleCollapsed", new ToggleCollapsedAction());
		return map;
	}

	@Override
	public void uninstallUI(JComponent c) {
		uninstallListeners();
		super.uninstallUI(c);
	}

	/**
	 * Uninstalls previously installed listeners to free component for garbage
	 * collection.
	 */
	protected void uninstallListeners() {
		group.removeMouseListener(mouseListener);
		group.removeMouseMotionListener(mouseListener);
		group.removeFocusListener(focusListener);
		group.removePropertyChangeListener(propertyListener);
	}

	/**
	 * Creates new toggle listener.
	 * 
	 * @return MouseInputListener reacting on toggle events of task pane.
	 */
	protected MouseInputListener createMouseInputListener() {
		return new ToggleListener();
	}

	/**
	 * Creates property change listener for task pane.
	 * 
	 * @return Property change listener reacting on changes to the task pane.
	 */
	protected PropertyChangeListener createPropertyListener() {
		return new ChangeListener();
	}

	/**
	 * Evaluates whenever given mouse even have occurred within borders of task
	 * pane.
	 * 
	 * @param event
	 *            Evaluated event.
	 * @return True if event occurred within task pane area, false otherwise.
	 */
	protected boolean isInBorder(MouseEvent event) {
		return event.getY() < getTitleHeight(event.getComponent());
	}

	/**
	 * Gets current title height. Default value is 25 if not specified
	 * otherwise. Method checks provided component for user set font
	 * (!instanceof FontUIResource), if font is set, height will be calculated
	 * from font metrics instead of using internal preset height.
	 * 
	 * @return Current title height.
	 */
	protected int getTitleHeight(Component c) {
		if (c instanceof JXTaskPane) {
			JXTaskPane taskPane = (JXTaskPane) c;
			Font font = taskPane.getFont();
			int height = titleHeight;

			if (font != null && !(font instanceof FontUIResource)) {
				height = Math.max(height, taskPane.getFontMetrics(font).getHeight());
			}

			Icon icon = taskPane.getIcon();

			if (icon != null) {
				height = Math.max(height, icon.getIconHeight() + 4);
			}

			return height;
		}

		return titleHeight;
	}

	/**
	 * Creates new border for task pane.
	 * 
	 * @return Fresh border on every call.
	 */
	protected Border createPaneBorder() {
		return new PaneBorder();
	}

	@Override
	public Dimension getPreferredSize(JComponent c) {
		Component component = group.getComponent(0);
		if (!(component instanceof JXCollapsiblePane)) {
			// something wrong in this JXTaskPane
			return super.getPreferredSize(c);
		}

		JXCollapsiblePane collapsible = (JXCollapsiblePane) component;
		Dimension dim = collapsible.getPreferredSize();

		Border groupBorder = group.getBorder();
		if (groupBorder instanceof PaneBorder) {
			((PaneBorder) groupBorder).label.setDisplayedMnemonic(group.getMnemonic());
			Dimension border = ((PaneBorder) groupBorder).getPreferredSize(group);
			dim.width = Math.max(dim.width, border.width);
			dim.height += border.height;
		} else {
			dim.height += getTitleHeight(c);
		}

		return dim;
	}

	/**
	 * Creates content pane border.
	 * 
	 * @return Fresh content pane border initialized with current value of
	 *         TaskPane.borderColor on every call.
	 */
	protected Border createContentPaneBorder() {
		Color borderColor = UIManager.getColor("TaskPane.borderColor");
		return new CompoundBorder(new ContentPaneBorder(borderColor), BorderFactory.createEmptyBorder(10, 10, 10, 10));
	}

	@Override
	public Component createAction(Action action) {
		JXHyperlink link = new JXHyperlink(action) {
			@Override
			public void updateUI() {
				super.updateUI();
				// ensure the ui of this link is correctly update on l&f changes
				configure(this);
			}
		};
		configure(link);
		return link;
	}

	/**
	 * Configures internally used hyperlink on new action creation and on every
	 * call to <code>updateUI()</code>.
	 * 
	 * @param link
	 *            Configured hyperlink.
	 */
	protected void configure(JXHyperlink link) {
		link.setOpaque(false);
		link.setBorderPainted(false);
		link.setFocusPainted(true);
		link.setForeground(UIManager.getColor("TaskPane.titleForeground"));
	}

	/**
	 * Ensures expanded group is visible. Issues delayed request for scrolling
	 * to visible.
	 */
	protected void ensureVisible() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				group.scrollRectToVisible(new Rectangle(group.getWidth(), group.getHeight()));
			}
		});
	}

	/**
	 * Focus listener responsible for repainting of the taskpane on focus
	 * change.
	 */
	static class RepaintOnFocus implements FocusListener {
		@Override
		public void focusGained(FocusEvent e) {
			e.getComponent().repaint();
		}

		@Override
		public void focusLost(FocusEvent e) {
			e.getComponent().repaint();
		}
	}

	/**
	 * Change listener responsible for change handling.
	 */
	class ChangeListener implements PropertyChangeListener {
		@Override
		public void propertyChange(PropertyChangeEvent evt) {
			// if group is expanded but not animated
			// or if animated has reached expanded state
			// scroll to visible if scrollOnExpand is enabled
			if (("collapsed".equals(evt.getPropertyName()) && Boolean.TRUE.equals(evt.getNewValue()) && !group.isAnimated())) {
				if (group.isScrollOnExpand()) {
					ensureVisible();
				}
			} else if (JXTaskPane.ICON_CHANGED_KEY.equals(evt.getPropertyName())
					|| JXTaskPane.TITLE_CHANGED_KEY.equals(evt.getPropertyName())
					|| JXTaskPane.SPECIAL_CHANGED_KEY.equals(evt.getPropertyName())) {
				// icon, title, special must lead to a repaint()
				group.repaint();
			} else if ("mnemonic".equals(evt.getPropertyName())) {
				SwingXUtilities.updateMnemonicBinding(group, "toggleCollapsed");

				Border b = group.getBorder();

				if (b instanceof PaneBorder) {
					int key = (Integer) evt.getNewValue();
					((PaneBorder) b).label.setDisplayedMnemonic(key);
				}
			}
		}
	}

	/**
	 * Mouse listener responsible for handling of toggle events.
	 */
	class ToggleListener extends MouseInputAdapter {
		@Override
		public void mouseEntered(MouseEvent e) {
			if (isInBorder(e)) {
				e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
			} else {
				mouseOver = false;
				group.repaint(0, 0, group.getWidth(), getTitleHeight(group));
			}
		}

		@Override
		public void mouseExited(MouseEvent e) {
			e.getComponent().setCursor(null);
			mouseOver = false;
			group.repaint(0, 0, group.getWidth(), getTitleHeight(group));
		}

		@Override
		public void mouseMoved(MouseEvent e) {
			if (isInBorder(e)) {
				e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
				mouseOver = true;
			} else {
				e.getComponent().setCursor(null);
				mouseOver = false;
			}

			group.repaint(0, 0, group.getWidth(), getTitleHeight(group));
		}

		@Override
		public void mouseReleased(MouseEvent e) {
			if (SwingUtilities.isLeftMouseButton(e) && isInBorder(e)) {
				group.setCollapsed(!group.isCollapsed());
			}
		}
	}

	/**
	 * Toggle expanded action.
	 */
	class ToggleCollapsedAction extends AbstractAction {
		/**
		 * Serial version UID.
		 */
		private static final long serialVersionUID = 5676859881615358815L;

		public ToggleCollapsedAction() {
			super("toggleCollapsed");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			group.setCollapsed(!group.isCollapsed());
		}

		@Override
		public boolean isEnabled() {
			return group.isVisible();
		}
	}

	/**
	 * Toggle icon.
	 */
	protected static class ChevronIcon implements Icon {
		boolean up = true;

		public ChevronIcon(boolean up) {
			this.up = up;
		}

		@Override
		public int getIconHeight() {
			return 3;
		}

		@Override
		public int getIconWidth() {
			return 6;
		}

		@Override
		public void paintIcon(Component c, Graphics g, int x, int y) {
			if (up) {
				g.drawLine(x + 3, y, x, y + 3);
				g.drawLine(x + 3, y, x + 6, y + 3);
			} else {
				g.drawLine(x, y, x + 3, y + 3);
				g.drawLine(x + 3, y + 3, x + 6, y);
			}
		}
	}

	/**
	 * The border around the content pane
	 */
	protected static class ContentPaneBorder implements Border, UIResource {
		Color color;

		public ContentPaneBorder(Color color) {
			this.color = color;
		}

		@Override
		public Insets getBorderInsets(Component c) {
			return new Insets(0, 1, 1, 1);
		}

		@Override
		public boolean isBorderOpaque() {
			return true;
		}

		@Override
		public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
			g.setColor(color);
			g.drawLine(x, y, x, y + height - 1);
			g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
			g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
		}
	}

	/**
	 * The border of the taskpane group paints the "text", the "icon", the
	 * "expanded" status and the "special" type.
	 * 
	 */
	protected class PaneBorder implements Border, UIResource {

		protected Color borderColor;
		protected Color titleForeground;
		protected Color specialTitleBackground;
		protected Color specialTitleForeground;
		protected Color titleBackgroundGradientStart;
		protected Color titleBackgroundGradientEnd;

		protected Color titleOver;
		protected Color specialTitleOver;

		protected JLabel label;

		/**
		 * Creates new instance of individual pane border.
		 */
		public PaneBorder() {
			borderColor = UIManager.getColor("TaskPane.borderColor");

			titleForeground = UIManager.getColor("TaskPane.titleForeground");

			specialTitleBackground = UIManager.getColor("TaskPane.specialTitleBackground");
			specialTitleForeground = UIManager.getColor("TaskPane.specialTitleForeground");

			titleBackgroundGradientStart = UIManager.getColor("TaskPane.titleBackgroundGradientStart");
			titleBackgroundGradientEnd = UIManager.getColor("TaskPane.titleBackgroundGradientEnd");

			titleOver = UIManager.getColor("TaskPane.titleOver");
			if (titleOver == null) {
				titleOver = specialTitleBackground.brighter();
			}
			specialTitleOver = UIManager.getColor("TaskPane.specialTitleOver");
			if (specialTitleOver == null) {
				specialTitleOver = specialTitleBackground.brighter();
			}

			label = new JLabel();
			label.setOpaque(false);
			label.setIconTextGap(8);
		}

		@Override
		public Insets getBorderInsets(Component c) {
			return new Insets(getTitleHeight(c), 0, 0, 0);
		}

		/**
		 * Overwritten to always return <code>true</code> to speed up painting.
		 * Don't use transparent borders unless providing UI delegate that
		 * provides proper return value when calling this method.
		 * 
		 * @see javax.swing.border.Border#isBorderOpaque()
		 */
		@Override
		public boolean isBorderOpaque() {
			return true;
		}

		/**
		 * Calculates the preferred border size, its size so all its content
		 * fits.
		 * 
		 * @param group
		 *            Selected group.
		 */
		public Dimension getPreferredSize(JXTaskPane group) {
			// calculate the title width so it is fully visible
			// it starts with the title width
			configureLabel(group);
			Dimension dim = label.getPreferredSize();
			// add the title left offset
			dim.width += 3;
			// add the controls width
			dim.width += getTitleHeight(group);
			// and some space between label and controls
			dim.width += 3;

			dim.height = getTitleHeight(group);
			return dim;
		}

		/**
		 * Paints background of the title. This may differ based on properties
		 * of the group.
		 * 
		 * @param group
		 *            Selected group.
		 * @param g
		 *            Target graphics.
		 */
		protected void paintTitleBackground(JXTaskPane group, Graphics g) {
			if (group.isSpecial()) {
				g.setColor(specialTitleBackground);
			} else {
				g.setColor(titleBackgroundGradientStart);
			}
			g.fillRect(0, 0, group.getWidth(), getTitleHeight(group) - 1);
		}

		/**
		 * Paints current group title.
		 * 
		 * @param group
		 *            Selected group.
		 * @param g
		 *            Target graphics.
		 * @param textColor
		 *            Title color.
		 * @param x
		 *            X coordinate of the top left corner.
		 * @param y
		 *            Y coordinate of the top left corner.
		 * @param width
		 *            Width of the box.
		 * @param height
		 *            Height of the box.
		 */
		protected void paintTitle(JXTaskPane group, Graphics g, Color textColor, int x, int y, int width, int height) {
			configureLabel(group);
			label.setForeground(textColor);
			if (group.getFont() != null && !(group.getFont() instanceof FontUIResource)) {
				label.setFont(group.getFont());
			}
			g.translate(x, y);
			label.setBounds(0, 0, width, height);
			label.paint(g);
			g.translate(-x, -y);
		}

		/**
		 * Configures label for the group using its title, font, icon and
		 * orientation.
		 * 
		 * @param group
		 *            Selected group.
		 */
		protected void configureLabel(JXTaskPane group) {
			label.applyComponentOrientation(group.getComponentOrientation());
			label.setFont(group.getFont());
			label.setText(group.getTitle());
			label.setIcon(group.getIcon() == null ? new EmptyIcon() : group.getIcon());
		}

		/**
		 * Paints expanded controls. Default implementation does nothing.
		 * 
		 * @param group
		 *            Expanded group.
		 * @param g
		 *            Target graphics.
		 * @param x
		 *            X coordinate of the top left corner.
		 * @param y
		 *            Y coordinate of the top left corner.
		 * @param width
		 *            Width of the box.
		 * @param height
		 *            Height of the box.
		 */
		protected void paintExpandedControls(JXTaskPane group, Graphics g, int x, int y, int width, int height) {
		}

		/**
		 * Gets current paint color.
		 * 
		 * @param group
		 *            Selected group.
		 * @return Color to be used for painting provided group.
		 */
		protected Color getPaintColor(JXTaskPane group) {
			Color paintColor;
			if (isMouseOverBorder()) {
				if (mouseOver) {
					if (group.isSpecial()) {
						paintColor = specialTitleOver;
					} else {
						paintColor = titleOver;
					}
				} else {
					if (group.isSpecial()) {
						paintColor = specialTitleForeground;
					} else {
						paintColor = group.getForeground() == null || group.getForeground() instanceof ColorUIResource ? titleForeground
								: group.getForeground();
					}
				}
			} else {
				if (group.isSpecial()) {
					paintColor = specialTitleForeground;
				} else {
					paintColor = group.getForeground() == null || group.getForeground() instanceof ColorUIResource ? titleForeground
							: group.getForeground();
				}
			}
			return paintColor;
		}

		/*
		 * @see javax.swing.border.Border#paintBorder(java.awt.Component,
		 * java.awt.Graphics, int, int, int, int)
		 */
		@Override
		public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {

			JXTaskPane group = (JXTaskPane) c;

			// calculate position of title and toggle controls
			int controlWidth = getTitleHeight(group) - 2 * getRoundHeight();
			int controlX = group.getWidth() - getTitleHeight(group);
			int controlY = getRoundHeight() - 1;
			int titleX = 3;
			int titleY = 0;
			int titleWidth = group.getWidth() - getTitleHeight(group) - 3;
			int titleHeight = getTitleHeight(group);

			if (!group.getComponentOrientation().isLeftToRight()) {
				controlX = group.getWidth() - controlX - controlWidth;
				titleX = group.getWidth() - titleX - titleWidth;
			}

			// paint the title background
			paintTitleBackground(group, g);

			// paint the the toggles
			paintExpandedControls(group, g, controlX, controlY, controlWidth, controlWidth);

			// paint the title text and icon
			Color paintColor = getPaintColor(group);

			// focus painted same color as text
			if (group.hasFocus()) {
				paintFocus(g, paintColor, 3, 3, width - 6, getTitleHeight(group) - 6);
			}

			paintTitle(group, g, paintColor, titleX, titleY, titleWidth, titleHeight);
		}

		/**
		 * Paints oval 'border' area around the control itself.
		 * 
		 * @param group
		 *            Expanded group.
		 * @param g
		 *            Target graphics.
		 * @param x
		 *            X coordinate of the top left corner.
		 * @param y
		 *            Y coordinate of the top left corner.
		 * @param width
		 *            Width of the box.
		 * @param height
		 *            Height of the box.
		 */
		protected void paintRectAroundControls(JXTaskPane group, Graphics g, int x, int y, int width, int height,
				Color highColor, Color lowColor) {
			if (mouseOver) {
				int x2 = x + width;
				int y2 = y + height;
				g.setColor(highColor);
				g.drawLine(x, y, x2, y);
				g.drawLine(x, y, x, y2);
				g.setColor(lowColor);
				g.drawLine(x2, y, x2, y2);
				g.drawLine(x, y2, x2, y2);
			}
		}

		/**
		 * Paints oval 'border' area around the control itself.
		 * 
		 * @param group
		 *            Expanded group.
		 * @param g
		 *            Target graphics.
		 * @param x
		 *            X coordinate of the top left corner.
		 * @param y
		 *            Y coordinate of the top left corner.
		 * @param width
		 *            Width of the box.
		 * @param height
		 *            Height of the box.
		 */
		protected void paintOvalAroundControls(JXTaskPane group, Graphics g, int x, int y, int width, int height) {
			if (group.isSpecial()) {
				g.setColor(specialTitleBackground.brighter());
				g.drawOval(x, y, width, height);
			} else {
				g.setColor(titleBackgroundGradientStart);
				g.fillOval(x, y, width, height);

				g.setColor(titleBackgroundGradientEnd.darker());
				g.drawOval(x, y, width, width);
			}
		}

		/**
		 * Paints controls for the group.
		 * 
		 * @param group
		 *            Expanded group.
		 * @param g
		 *            Target graphics.
		 * @param x
		 *            X coordinate of the top left corner.
		 * @param y
		 *            Y coordinate of the top left corner.
		 * @param width
		 *            Width of the box.
		 * @param height
		 *            Height of the box.
		 */
		protected void paintChevronControls(JXTaskPane group, Graphics g, int x, int y, int width, int height) {
			ChevronIcon chevron;
			if (group.isCollapsed()) {
				chevron = new ChevronIcon(false);
			} else {
				chevron = new ChevronIcon(true);
			}
			int chevronX = x + width / 2 - chevron.getIconWidth() / 2;
			int chevronY = y + (height / 2 - chevron.getIconHeight());
			chevron.paintIcon(group, g, chevronX, chevronY);
			chevron.paintIcon(group, g, chevronX, chevronY + chevron.getIconHeight() + 1);
		}

		/**
		 * Paints focused group.
		 * 
		 * @param g
		 *            Target graphics.
		 * @param paintColor
		 *            Focused group color.
		 * @param x
		 *            X coordinate of the top left corner.
		 * @param y
		 *            Y coordinate of the top left corner.
		 * @param width
		 *            Width of the box.
		 * @param height
		 *            Height of the box.
		 */
		protected void paintFocus(Graphics g, Color paintColor, int x, int y, int width, int height) {
			g.setColor(paintColor);
			BasicGraphicsUtils.drawDashedRect(g, x, y, width, height);
		}

		/**
		 * Default implementation returns false.
		 * 
		 * @return true if this border wants to display things differently when
		 *         the mouse is over it
		 */
		protected boolean isMouseOverBorder() {
			return false;
		}
	}

	/**
	 * Gets size of arc used to round corners.
	 * 
	 * @return size of arc used to round corners of the panel.
	 */
	protected int getRoundHeight() {
		return roundHeight;
	}

}
